/* ========================================================================
 * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
 *
 * OPC Foundation MIT License 1.00
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * The complete license agreement can be found here:
 * http://opcfoundation.org/License/MIT/1.00/
 * ======================================================================*/

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using NUnit.Framework;
using Opc.Ua.Security.Certificates;
using Opc.Ua.Security.Certificates.Tests;
using Opc.Ua.Tests;
using Assert = NUnit.Framework.Legacy.ClassicAssert;
using X509AuthorityKeyIdentifierExtension = Opc.Ua.Security.Certificates.X509AuthorityKeyIdentifierExtension;

namespace Opc.Ua.Core.Tests.Security.Certificates
{
    /// <summary>
    /// Tests for the CertificateFactory class.
    /// </summary>
    [TestFixture]
    [Category("CertificateFactory")]
    [Parallelizable]
    [SetCulture("en-us")]
    public class CertificateFactoryTest
    {
        [DatapointSource]
        public KeyHashPair[] KeyHashPairs = new KeyHashPairCollection
        {
            { 2048, HashAlgorithmName.SHA256 },
            { 3072, HashAlgorithmName.SHA384 },
            { 4096, HashAlgorithmName.SHA512 }
        }.ToArray();

        /// <summary>
        /// Create a dictionary for certificates.
        /// </summary>
        [OneTimeSetUp]
        protected void OneTimeSetUp()
        {
            m_rootCACertificate = new ConcurrentDictionary<int, X509Certificate2>();
        }

        /// <summary>
        /// One time cleanup.
        /// </summary>
        [OneTimeTearDown]
        protected void OneTimeTearDown()
        {
            foreach (X509Certificate2 cert in m_rootCACertificate.Values)
            {
                Utils.SilentDispose(cert);
            }
        }

        /// <summary>
        /// Verify self signed app certs.
        /// </summary>
        [Theory]
        public void VerifySelfSignedAppCerts(KeyHashPair keyHashPair)
        {
            ITelemetryContext telemetry = NUnitTelemetryContext.Create();

            var appTestGenerator = new ApplicationTestDataGenerator(keyHashPair.KeySize, telemetry);
            ApplicationTestData app = appTestGenerator.ApplicationTestSet(1).First();
            using X509Certificate2 cert = CertificateFactory
                .CreateCertificate(
                    app.ApplicationUri,
                    app.ApplicationName,
                    app.Subject,
                    app.DomainNames)
                .SetHashAlgorithm(keyHashPair.HashAlgorithmName)
                .SetRSAKeySize(keyHashPair.KeySize)
                .CreateForRSA();
            Assert.NotNull(cert);
            Assert.NotNull(cert.RawData);
            Assert.True(cert.HasPrivateKey);
            using (RSA rsa = cert.GetRSAPrivateKey())
            {
                rsa.ExportParameters(true);
            }
            using (RSA rsa = cert.GetRSAPublicKey())
            {
                rsa.ExportParameters(false);
            }
            X509Certificate2 plainCert = CertificateFactory.Create(cert.RawData);
            Assert.NotNull(plainCert);
            VerifyApplicationCert(app, plainCert);
            X509Utils.VerifyRSAKeyPair(cert, cert, true);
            Assert.True(X509Utils.VerifySelfSigned(cert), "Verify Self signed.");
        }

        /// <summary>
        /// Verify signed OPC UA app certs.
        /// </summary>
        [Theory]
        [Order(500)]
        public void VerifySignedAppCerts(KeyHashPair keyHashPair)
        {
            ITelemetryContext telemetry = NUnitTelemetryContext.Create();

            X509Certificate2 issuerCertificate = GetIssuer(keyHashPair);
            Assert.NotNull(issuerCertificate);
            Assert.NotNull(issuerCertificate.RawData);
            Assert.True(issuerCertificate.HasPrivateKey);
            var appTestGenerator = new ApplicationTestDataGenerator(keyHashPair.KeySize, telemetry);
            ApplicationTestData app = appTestGenerator.ApplicationTestSet(1).First();
            using X509Certificate2 cert = CertificateFactory
                .CreateCertificate(
                    app.ApplicationUri,
                    app.ApplicationName,
                    app.Subject,
                    app.DomainNames)
                .SetHashAlgorithm(keyHashPair.HashAlgorithmName)
                .SetIssuer(issuerCertificate)
                .SetRSAKeySize(keyHashPair.KeySize)
                .CreateForRSA();
            Assert.NotNull(cert);
            Assert.NotNull(cert.RawData);
            Assert.True(cert.HasPrivateKey);
            using X509Certificate2 plainCert = CertificateFactory.Create(cert.RawData);
            Assert.NotNull(plainCert);
            VerifyApplicationCert(app, plainCert, issuerCertificate);
            X509Utils.VerifyRSAKeyPair(plainCert, cert, true);
        }

        /// <summary>
        /// Verify CA signed app certs.
        /// </summary>
        [Theory]
        [Order(100)]
        public void VerifyCACerts(KeyHashPair keyHashPair)
        {
            const string subject = "CN=CA Test Cert,O=OPC Foundation,C=US,S=Arizona";
            int pathLengthConstraint = (keyHashPair.KeySize / 512) - 3;
            X509Certificate2 cert = CertificateFactory
                .CreateCertificate(subject)
                .SetLifeTime(25 * 12)
                .SetHashAlgorithm(keyHashPair.HashAlgorithmName)
                .SetCAConstraint(pathLengthConstraint)
                .SetRSAKeySize(keyHashPair.KeySize)
                .CreateForRSA();
            Assert.NotNull(cert);
            Assert.NotNull(cert.RawData);
            Assert.True(cert.HasPrivateKey);
            X509Certificate2 plainCert = CertificateFactory.Create(cert.RawData);
            Assert.NotNull(plainCert);
            VerifyCACert(plainCert, subject, pathLengthConstraint);
            X509Utils.VerifyRSAKeyPair(cert, cert, true);
            Assert.True(X509Utils.VerifySelfSigned(cert));
            m_rootCACertificate[keyHashPair.KeySize] = cert;
        }

        /// <summary>
        /// Verify CRL for CA signed app certs.
        /// </summary>
        [Theory]
        [Order(400)]
        public void VerifyCrlCerts(KeyHashPair keyHashPair)
        {
            int pathLengthConstraint = (keyHashPair.KeySize / 512) - 3;
            X509Certificate2 issuerCertificate = GetIssuer(keyHashPair);
            Assert.True(X509Utils.VerifySelfSigned(issuerCertificate));

            using X509Certificate2 otherIssuerCertificate = CertificateFactory
                .CreateCertificate(issuerCertificate.Subject)
                .SetLifeTime(TimeSpan.FromDays(180))
                .SetHashAlgorithm(keyHashPair.HashAlgorithmName)
                .SetCAConstraint(pathLengthConstraint)
                .CreateForRSA();
            Assert.True(X509Utils.VerifySelfSigned(otherIssuerCertificate));

            var revokedCerts = new X509Certificate2Collection();
            try
            {
                for (int i = 0; i < 10; i++)
                {
                    X509Certificate2 cert = CertificateFactory
                        .CreateCertificate($"CN=Test Cert {i}, O=Contoso")
                        .SetIssuer(issuerCertificate)
                        .SetRSAKeySize(
                            (ushort)(keyHashPair.KeySize <= 2048 ? keyHashPair.KeySize : 2048))
                        .CreateForRSA();
                    revokedCerts.Add(cert);
                    Assert.False(X509Utils.VerifySelfSigned(cert));
                }

                Assert.NotNull(issuerCertificate);
                Assert.NotNull(issuerCertificate.RawData);
                Assert.True(issuerCertificate.HasPrivateKey);
                using (RSA rsa = issuerCertificate.GetRSAPrivateKey())
                {
                    Assert.NotNull(rsa);
                }

                using (X509Certificate2 plainCert = CertificateFactory.Create(
                    issuerCertificate.RawData))
                {
                    Assert.NotNull(plainCert);
                    VerifyCACert(plainCert, issuerCertificate.Subject, pathLengthConstraint);
                }
                Assert.True(X509Utils.VerifySelfSigned(issuerCertificate));
                X509Utils.VerifyRSAKeyPair(issuerCertificate, issuerCertificate, true);

                X509CRL crl = CertificateFactory.RevokeCertificate(issuerCertificate, null, null);
                Assert.NotNull(crl);
                Assert.True(crl.VerifySignature(issuerCertificate, true));
                X509CrlNumberExtension extension = crl.CrlExtensions
                    .FindExtension<X509CrlNumberExtension>();
                var crlCounter = new BigInteger(1);
                Assert.AreEqual(crlCounter, extension.CrlNumber);
                var revokedList = new X509CRLCollection { crl };

                foreach (X509Certificate2 cert in revokedCerts)
                {
                    NUnit.Framework.Assert.Throws<CryptographicException>(() =>
                        crl.VerifySignature(otherIssuerCertificate, true));
                    Assert.False(crl.IsRevoked(cert));
                    X509CRL nextCrl = CertificateFactory.RevokeCertificate(
                        issuerCertificate,
                        revokedList,
                        new X509Certificate2Collection(cert));
                    crlCounter++;
                    Assert.NotNull(nextCrl);
                    Assert.True(nextCrl.IsRevoked(cert));
                    extension = nextCrl.CrlExtensions.FindExtension<X509CrlNumberExtension>();
                    Assert.AreEqual(crlCounter, extension.CrlNumber);
                    Assert.True(crl.VerifySignature(issuerCertificate, true));
                    revokedList.Add(nextCrl);
                    crl = nextCrl;
                }

                foreach (X509Certificate2 cert in revokedCerts)
                {
                    Assert.True(crl.IsRevoked(cert));
                }
            }
            finally
            {
                foreach (X509Certificate2 cert in revokedCerts)
                {
                    Utils.SilentDispose(cert);
                }
            }
        }

        /// <summary>
        /// Parse a certificate blob.
        /// </summary>
        [Test]
        [Order(500)]
        public void ParseCertificateBlob()
        {
            ITelemetryContext telemetry = NUnitTelemetryContext.Create();

            // check if complete chain should be sent.
            if (m_rootCACertificate != null && !m_rootCACertificate.IsEmpty)
            {
                X509Certificate2[] certArray = [.. m_rootCACertificate.Values];

                TestContext.Out.WriteLine("testing {0} certificates", certArray.Length);

                byte[] certBlob = Utils.CreateCertificateChainBlob([.. certArray]);

                byte[] singleBlob = AsnUtils.ParseX509Blob(certBlob).ToArray();
                Assert.NotNull(singleBlob);
                X509Certificate2 certX = CertificateFactory.Create(singleBlob);
                Assert.NotNull(certX);
                Assert.AreEqual(certArray[0].RawData, singleBlob);
                Assert.AreEqual(singleBlob, certX.RawData);
                Assert.AreEqual(certArray[0].RawData, certX.RawData);

                X509Certificate2 cert = Utils.ParseCertificateBlob(certBlob, telemetry);
                Assert.NotNull(cert);
                Assert.AreEqual(cert.RawData, certArray[0].RawData);
                X509Certificate2Collection certChain = Utils.ParseCertificateChainBlob(certBlob, telemetry);
                Assert.NotNull(certChain);
                for (int i = 0; i < certArray.Length; i++)
                {
                    TestContext.Out.WriteLine(certChain[i]);
                    Assert.AreEqual(certChain[i].RawData, certArray[i].RawData);
                }
            }
            else
            {
                NUnit.Framework.Assert.Ignore("No certificates for blob test");
            }
        }

        [Test]
        [Category("X509Utils")]
        public void CompareDistinguishedNameWithStateAbbreviations()
        {
            // Test that ST= and S= are treated as equivalent for stateOrProvinceName
            // This addresses the Windows behavior where S= is used instead of ST=

            // Test 1: Direct comparison with different state abbreviations
            string dnWithST = "CN=OPCUA Client, O=MyOrg, ST=California, C=US";
            string dnWithS = "CN=OPCUA Client, O=MyOrg, S=California, C=US";

            Assert.True(X509Utils.CompareDistinguishedName(dnWithST, dnWithS),
                "Distinguished names with ST= and S= should be considered equivalent");

            // Test 2: Reverse comparison
            Assert.True(X509Utils.CompareDistinguishedName(dnWithS, dnWithST),
                "Distinguished names with S= and ST= should be considered equivalent (reversed)");

            // Test 3: With parsed distinguished names
            List<string> parsedDnWithST = X509Utils.ParseDistinguishedName(dnWithST);
            List<string> parsedDnWithS = X509Utils.ParseDistinguishedName(dnWithS);

            Assert.NotNull(parsedDnWithST);
            Assert.NotNull(parsedDnWithS);

            // Test 4: Case sensitivity for DC fields should still work
            string dnWithDC1 = "CN=Test, DC=example, DC=com";
            string dnWithDC2 = "CN=Test, DC=EXAMPLE, DC=COM";

            Assert.True(X509Utils.CompareDistinguishedName(dnWithDC1, dnWithDC2),
                "DC fields should be case-insensitive");

            // Test 5: Different values should still fail
            string dnDifferentState1 = "CN=OPCUA Client, O=MyOrg, ST=California, C=US";
            string dnDifferentState2 = "CN=OPCUA Client, O=MyOrg, ST=NewYork, C=US";

            Assert.False(X509Utils.CompareDistinguishedName(dnDifferentState1, dnDifferentState2),
                "Distinguished names with different state values should not match");

            // Test 6: Case with other fields
            string dnComplex1 = "CN=Server, OU=Engineering, O=Company, ST=Texas, L=Austin, C=US";
            string dnComplex2 = "CN=Server, OU=Engineering, O=Company, S=Texas, L=Austin, C=US";

            Assert.True(X509Utils.CompareDistinguishedName(dnComplex1, dnComplex2),
                "Complex DN with ST= and S= should match");
        }

        private X509Certificate2 GetIssuer(KeyHashPair keyHashPair)
        {
            X509Certificate2 issuerCertificate = null;
            try
            {
                if (!m_rootCACertificate.TryGetValue(keyHashPair.KeySize, out issuerCertificate))
                {
                    VerifyCACerts(keyHashPair);
                    if (!m_rootCACertificate.TryGetValue(
                        keyHashPair.KeySize,
                        out issuerCertificate))
                    {
                        NUnit.Framework.Assert.Ignore("Could not load Issuer Cert.");
                    }
                }
            }
            catch
            {
                NUnit.Framework.Assert.Ignore("Could not load create Issuer Cert.");
            }
            return issuerCertificate;
        }

        public static void VerifyApplicationCert(
            ApplicationTestData testApp,
            X509Certificate2 cert,
            X509Certificate2 issuerCert = null)
        {
            bool signedCert = issuerCert != null;
            issuerCert ??= cert;
            TestContext.Out.WriteLine($"{nameof(VerifyApplicationCert)}:");
            Assert.NotNull(cert);
            TestContext.Out.WriteLine(cert);
            Assert.False(cert.HasPrivateKey);
            Assert.True(X509Utils.CompareDistinguishedName(testApp.Subject, cert.Subject));
            Assert.True(X509Utils.CompareDistinguishedName(issuerCert.Subject, cert.Issuer));

            Assert.AreEqual(cert.IssuerName.Name, issuerCert.SubjectName.Name);
            Assert.AreEqual(cert.IssuerName.RawData, issuerCert.SubjectName.RawData);

            // test basic constraints
            X509BasicConstraintsExtension constraints = cert
                .FindExtension<X509BasicConstraintsExtension>();
            Assert.NotNull(constraints);
            TestContext.Out.WriteLine(constraints.Format(true));
            Assert.True(constraints.Critical);
            if (signedCert)
            {
                Assert.False(constraints.CertificateAuthority);
                Assert.False(constraints.HasPathLengthConstraint);
            }
            else
            {
                Assert.False(constraints.CertificateAuthority);
                Assert.False(constraints.HasPathLengthConstraint);
            }

            // key usage
            X509KeyUsageExtension keyUsage = cert.FindExtension<X509KeyUsageExtension>();
            Assert.NotNull(keyUsage);
            TestContext.Out.WriteLine(keyUsage.Format(true));
            Assert.True(keyUsage.Critical);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.CrlSign) == 0);
            Assert.True(
                (keyUsage.KeyUsages &
                    X509KeyUsageFlags.DataEncipherment) == X509KeyUsageFlags.DataEncipherment);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.DecipherOnly) == 0);
            Assert.True(
                (keyUsage.KeyUsages &
                    X509KeyUsageFlags.DigitalSignature) == X509KeyUsageFlags.DigitalSignature);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.EncipherOnly) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.KeyAgreement) == 0);
            Assert.True(
                (keyUsage.KeyUsages & X509KeyUsageFlags.KeyCertSign) == (signedCert
                    ? 0
                    : X509KeyUsageFlags.KeyCertSign));
            Assert.True((keyUsage.KeyUsages &
                X509KeyUsageFlags.KeyEncipherment) == X509KeyUsageFlags.KeyEncipherment);
            Assert.True((keyUsage.KeyUsages &
                X509KeyUsageFlags.NonRepudiation) == X509KeyUsageFlags.NonRepudiation);

            // enhanced key usage
            X509EnhancedKeyUsageExtension enhancedKeyUsage = cert
                .FindExtension<X509EnhancedKeyUsageExtension>();
            Assert.NotNull(enhancedKeyUsage);
            TestContext.Out.WriteLine(enhancedKeyUsage.Format(true));
            Assert.True(enhancedKeyUsage.Critical);

            // test for authority key

            X509AuthorityKeyIdentifierExtension authority = cert
                .FindExtension<X509AuthorityKeyIdentifierExtension>();
            Assert.NotNull(authority);
            TestContext.Out.WriteLine(authority.Format(true));
            Assert.NotNull(authority.SerialNumber);
            Assert.NotNull(authority.KeyIdentifier);
            Assert.NotNull(authority.Issuer);
            if (issuerCert == null)
            {
                Assert.AreEqual(cert.SubjectName.RawData, authority.Issuer.RawData);
                Assert.True(
                    X509Utils.CompareDistinguishedName(
                        cert.SubjectName.Name,
                        authority.Issuer.Name),
                    $"{cert.SubjectName.Name} != {authority.Issuer.Name}");
            }
            else
            {
                Assert.AreEqual(issuerCert.SubjectName.RawData, authority.Issuer.RawData);
                Assert.True(
                    X509Utils.CompareDistinguishedName(
                        issuerCert.SubjectName.Name,
                        authority.Issuer.Name),
                    $"{cert.SubjectName.Name} != {authority.Issuer.Name}");
            }

            // verify authority key in signed cert
            X509SubjectKeyIdentifierExtension subjectKeyId = cert
                .FindExtension<X509SubjectKeyIdentifierExtension>();
            TestContext.Out.WriteLine(subjectKeyId.Format(true));
            if (signedCert)
            {
                X509SubjectKeyIdentifierExtension caCertSubjectKeyId =
                    issuerCert.FindExtension<X509SubjectKeyIdentifierExtension>();
                Assert.NotNull(caCertSubjectKeyId);
                Assert.AreEqual(caCertSubjectKeyId.SubjectKeyIdentifier, authority.KeyIdentifier);
            }
            else
            {
                Assert.AreEqual(subjectKeyId.SubjectKeyIdentifier, authority.KeyIdentifier);
            }
            Assert.AreEqual(issuerCert.GetSerialNumber(), authority.GetSerialNumber());
            Assert.AreEqual(issuerCert.SerialNumber, authority.SerialNumber);

            X509SubjectAltNameExtension subjectAlternateName = cert
                .FindExtension<X509SubjectAltNameExtension>();
            Assert.NotNull(subjectAlternateName);
            TestContext.Out.WriteLine(subjectAlternateName.Format(true));
            Assert.False(subjectAlternateName.Critical);
            IList<string> domainNames = X509Utils.GetDomainsFromCertificate(cert);
            foreach (string domainName in testApp.DomainNames)
            {
                Assert.True(domainNames.Contains(domainName, StringComparer.OrdinalIgnoreCase));
            }
            Assert.True(subjectAlternateName.Uris.Count == 1);
            IReadOnlyList<string> applicationUris = X509Utils.GetApplicationUrisFromCertificate(cert);
            string applicationUri = applicationUris.Count > 0 ? applicationUris[0] : null;
            TestContext.Out.WriteLine("ApplicationUris: ");
            TestContext.Out.WriteLine(applicationUri);
            Assert.AreEqual(testApp.ApplicationUri, applicationUri);
        }

        public static void VerifyCACert(
            X509Certificate2 cert,
            string subject,
            int pathLengthConstraint)
        {
            TestContext.Out.WriteLine($"{nameof(VerifyCACert)}:");

            Assert.NotNull(cert);
            TestContext.Out.WriteLine(cert);
            Assert.False(cert.HasPrivateKey);
            Assert.True(X509Utils.CompareDistinguishedName(subject, cert.Subject));
            Assert.True(X509Utils.CompareDistinguishedName(subject, cert.Issuer));

            Assert.AreEqual(cert.Subject, cert.Issuer);
            Assert.AreEqual(cert.SubjectName.RawData, cert.IssuerName.RawData);

            // test basic constraints
            X509BasicConstraintsExtension constraints = cert
                .FindExtension<X509BasicConstraintsExtension>();
            Assert.NotNull(constraints);
            TestContext.Out.WriteLine(constraints.Format(true));
            Assert.True(constraints.Critical);
            Assert.True(constraints.CertificateAuthority);
            if (pathLengthConstraint < 0)
            {
                Assert.False(constraints.HasPathLengthConstraint);
            }
            else
            {
                Assert.True(constraints.HasPathLengthConstraint);
                Assert.AreEqual(pathLengthConstraint, constraints.PathLengthConstraint);
            }

            // key usage
            X509KeyUsageExtension keyUsage = cert.FindExtension<X509KeyUsageExtension>();
            Assert.NotNull(keyUsage);
            TestContext.Out.WriteLine(keyUsage.Format(true));
            Assert.True(keyUsage.Critical);
            Assert.True(
                (keyUsage.KeyUsages & X509KeyUsageFlags.CrlSign) == X509KeyUsageFlags.CrlSign);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.DataEncipherment) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.DecipherOnly) == 0);
            Assert.True(
                (keyUsage.KeyUsages &
                    X509KeyUsageFlags.DigitalSignature) == X509KeyUsageFlags.DigitalSignature);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.EncipherOnly) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.KeyAgreement) == 0);
            Assert.True((keyUsage.KeyUsages &
                X509KeyUsageFlags.KeyCertSign) == X509KeyUsageFlags.KeyCertSign);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.KeyEncipherment) == 0);
            Assert.True((keyUsage.KeyUsages & X509KeyUsageFlags.NonRepudiation) == 0);

            // enhanced key usage
            X509EnhancedKeyUsageExtension enhancedKeyUsage = cert
                .FindExtension<X509EnhancedKeyUsageExtension>();
            Assert.Null(enhancedKeyUsage);

            // test for authority key

            X509AuthorityKeyIdentifierExtension authority = cert
                .FindExtension<X509AuthorityKeyIdentifierExtension>();
            Assert.NotNull(authority);
            TestContext.Out.WriteLine(authority.Format(true));
            Assert.NotNull(authority.SerialNumber);
            Assert.NotNull(authority.GetSerialNumber());
            Assert.NotNull(authority.KeyIdentifier);
            Assert.NotNull(authority.Issuer);
            Assert.AreEqual(cert.IssuerName.RawData, authority.Issuer.RawData);
            Assert.AreEqual(cert.IssuerName.Name, authority.Issuer.Name);
            Assert.NotNull(authority.ToString());
            Assert.AreEqual(
                authority.SerialNumber,
                Utils.ToHexString(authority.GetSerialNumber(), true));

            // verify authority key in signed cert
            X509SubjectKeyIdentifierExtension subjectKeyId = cert
                .FindExtension<X509SubjectKeyIdentifierExtension>();
            TestContext.Out.WriteLine(subjectKeyId.Format(true));
            Assert.AreEqual(subjectKeyId.SubjectKeyIdentifier, authority.KeyIdentifier);
            Assert.AreEqual(cert.SerialNumber, authority.SerialNumber);
            Assert.AreEqual(cert.GetSerialNumber(), authority.GetSerialNumber());

            X509SubjectAltNameExtension subjectAlternateName = cert
                .FindExtension<X509SubjectAltNameExtension>();
            Assert.Null(subjectAlternateName);
        }

        private ConcurrentDictionary<int, X509Certificate2> m_rootCACertificate;
    }
}
